import town
import route

DEF_TOWNS = 5

def _generate_route():
    """Generates a route for the world with a random cost"""
    r = route.Route()
    r.rand_cost_poly(3, 0, 4.641)
    return r
    

class World:
    def __init__(self):
        """Nothing for now """
       
        self._towns = None
        self._s_routes = None
        self._e_routes = None
        
    def populate(self, width = DEF_TOWNS, height = -1):
        """
        Populates a world with a grid of width by height towns and a set of
        routes that orthagonaly connect them.
        width - The width of the world (default = 5)
        height - The height of the world (default = -1)
        """
        
        if width < 0:
            raise Exception(
                "invalid arguement to World.populate, width must be >= 0")
        
        if height < 0:
            height = width
    
        self._towns = []
        self._s_routes = []
        self._e_routes = []
        
        for x in range(width):
            town_column = []
            e_column = []
            s_column = []
            
            for y in range(height):
                town_column.append(town.Town())
                r = route.Route()
                r.rand_cost_poly(3, 0, 4.641)
                e_column.append(_generate_route())
                if(y > 0):
                    s_column.append(_generate_route())
            
            self._towns.append(town_column)
            self._s_routes.append(s_column)
            if(x > 0):
                self._e_routes.append(e_column)
            
        
    def get_route(self, sourcex, sourcey, destinationx, destinationy):
        """
        Returns the route from a given source to a given destination.  The order
        of the source and destination is irrelevant, it will return the same
        route.
        sourcex, sourcey - The source coordinates
        destinationx, destinationy - The destination coordinates
        """
        
        for x in [sourcex, destinationx]:
            if x not in range(self.get_width()):
                return None
        for y in [sourcey, destinationy]:
            if y not in range(self.get_height()):
                return None
        
        
        changex = destinationx - sourcex
        changey = destinationy - sourcey
        
        for change in [changex, changey]:
            if change > 1 or change < -1:
                return None
        
        south = changey != 0
        east = changex != 0
        
        if south == east:
            return None
        
        x = min(destinationx, sourcex)
        y = min(destinationy, sourcey)
        
        if south:
            return self._get_south_route(x, y)
        else:
            return self._get_east_route(x, y)
        
    
    def get_town(self, x, y):
        """
        Returns the town at the given coordinate. none if the coordinates are
        out of range 
        """
        inX = x in range(self.get_width())
        inY = y in range(self.get_height())
        if not inX or not inY:
            return None
        return self._towns[x][y]
        
    def get_width(self):
        """
        Returns the number of towns wide the world is
        """
        if not self._towns:
            return 0
        return len(self._towns)
        
    def get_height(self):
        """
        Returns the number of towns high the world is
        """
        if not self.get_width():
            return 0
        return len(self._towns[0])
        
        
    def _get_south_route(self, x, y):
        """
        Returns the south route out of a given town coordinate
        """
        return self._s_routes[x][y]
    
    def _get_east_route(self, x, y):
        """
        Returns the east route out of given town coordinate
        """
        return self._e_routes[x][y]
        
    def to_all_towns(self, function, arguements = ()):
        """
        Runs the given function on all towns in the world.
        """
        for column in self._towns:
            for t in column:
                function(t, *arguements)
                
    def to_all_routes(self, function, arguements = ()):
        """
        Runs the given function on all routes in the world.
        """
        for set in [self._e_routes, self._s_routes]:
            for column in set:
                for r in column:
                    function(r, *arguements)
            
        
                
        
        
            
        
        
    
    
        
    